/**
 * options结构
 *  String  type           查询的类型, 读取配置决定查询的维度字段
 * 	Array   where          mongo查询条件, mongo查询语法条件结构
 * 	Array   metricWhere   指标查询条件, mongo查询语法条件结构
 * 	Array	dim_where      统计维度过滤条件, 避免用大量的mongo id作为查询条件
 * 	String  order          排序字符串, 逗号分隔多个
 * 	Number  page           要显示的分页
 * 	Number  size           分页大小
 * 	Array   fields         要显示的字段, 支持批量宏 {front}, {back}, ...
 * 	Array   exclude_fields 要过滤不查询的字段, 支持批量宏
 * 	String  ctype          查询溢价的方式, default: 最后溢价, copy: 代理溢价, admin: 所有溢价
 * 	Boolean query_amount   是否查询总计
 * 	Boolean query_items    是否查询项目数据
 * 	Array   base           基础查询参数配置对象, 直接填充到 Go 的查询参数中
 */

"use strict";

var sysUtil = require('util')
	,http   = require('http')
	,radon = require('radon')
	,include = radon.use('Include')
	,radon_json = require('./json64')
	,ModelDB 	= include.ModelDB
	,DB = require('./database')
	,util = radon.util
	,url = require('url')
	,log = util.log;

var hawkConfig = radon.config('hawk') || {};
var Int64 = radon.Int64;

radon_json = radon_json.parse ? radon_json : JSON;
var generator = util.generator;

var CONST = {
	'INCLUDE_MONGO_FIELDS': 1
	,'INCLUDE_METRIC_FIELDS': 2
	,'MAX_QUERY_LIMIT': 15000
};

// 查询核心模块
function DataCenterGo(c) {
	this.$debug = 0;
	this.$debug_data = [];
	this.$error_code = 0;
	var t = c || getConfig('server');

	if(!t) {
		setError(this, 10000002);
	}

	if(sysUtil.isString(t)) {
		t = url.parse(t);
	}

	this.$remote_config = t;
}
exports.base = DataCenterGo;

DataCenterGo.prototype.getError = function() {
	return this.$error_code;
};

DataCenterGo.prototype.getErrorMessage = function() {
	var msg = radon.error_message(this.$error_code);
	if (!msg){
		msg = 'Unknow Error Message Code: ' + this.$error_code;
	}
	return msg;
};

DataCenterGo.prototype.debug = function(mode, data) {
	var self = this;
	if(mode === undefined) {
		var msg = self.$debug_data;
		self.$debug_data = [];
		return msg;
	} else if (arguments.length > 1){
		if (self.$debug){
			var now = Date.now();
			if (self.$debug_ts === 0){
				self.$debug_ts = now;
			}
			mode += ':' + (now - self.$debug_ts);
			if (typeof(data) == 'object'){
				data = radon_json.parse(radon_json.stringify(data));
			}
			self.$debug_data.push(mode, data);
		}
	} else {
		self.$debug = mode ? 1 : 0;
		self.$debug_ts = 0;
	}

};

DataCenterGo.prototype.query = function(options) {
	var self = this;
	return self.queryWithException(options);
};



var go_log = [];

exports.log_record = go_log;

function *_query_by_go(options, param, mongoQuery) {
	var self = this;
	var goCond, goItems, goIds, mongoItems, mongoIds;
	// 指标维度过滤条件, 代替mongo id范围过滤
	var dimWhere = options['dim_where'];
	// 指标附加过滤条件
	var addOnWhere = options['addon_where'];

	// 有指标排序字段或有指标过滤参数
	// 维度过滤参数确定搜索条件
	if(dimWhere) {
		// 使用维度过滤条件查询数据
		goCond = extend(param.metricWhere, dimWhere, addOnWhere);
	}
	else {
		// 查询mongo条件 $mongoWhere 所有mongo id记录
		goCond = extend(param.metricWhere, addOnWhere);
		param._log.mongo_local_time = new Date();
		log(20, '开始查询Mongo 获取符合条件的_id 记录集:');
		mongoIds = yield mongoQuery.getResult(param.modelKey);
		param._log.mongo_local_time = new Date() - param._log.mongo_local_time;
		log(20, `结束查询Mongo 获取符合条件的_id 记录集, length: ${mongoIds.length}, 消耗时间: ${param._log.mongo_local_time}`);

		// 如果需要查询默认记录0
		if (options.zero)
		{
			mongoIds.push(0);
		}

		if(!mongoIds || !mongoIds.length)
		{
			return {
				getAmount: false
				,items: []
				,total: 0
				,goCond: null
			}
		}

		goCond[param.metricKey] = {'$in': mongoIds};
	}

	var must_keys = options.hasOwnProperty('must_keys') ?
			options.must_keys :
			(param.fullRecord || (!param.hasMetricWhere && param.sort.metric)) ? 1 : 0;

	var goOptions = {
		// 需要返回的指标字段
		'metrics': param.metricFields,
		// 查询过滤条件, 后面会附加过滤记录ID列表
		'conds': goCond,
		// 指标排序并且没有指标过滤时, 必须返回填 0 的记录避免无法显示项目
		'must_keys': must_keys,
		// 如果有mongo的排序记录, 返回结果必须按照记录顺序
		'in_keys_order': param.sort.mongo === undefined ? 0 : 1,
		// 分页大小, 默认无限
		'limit': CONST.MAX_QUERY_LIMIT,
		// 指标排序
		'orderby': param.sort.metric,
		// 返回items记录项目
		'use_item': 1
	};

	// 当前分页大于0, 计算分页参数
	var offset, total, data, item;
	var items = [];
	var getAmount;
	var hasPage = param.hasPage;
	if(hasPage) {
		offset = (param.page - 1) * param.size;
		if((param.hasMetricWhere && !param.fullRecord) || (options.amountByGo)) {
			param._log.go_count_time = new Date();
			// 调用Go接口, 查询记录总数
			log(20, '开始查询Go接口,获取记录总数');
			data = yield self.queryGo(
				{'conds': goCond, 'use_total': 1, metrics: param.metricFields},
				param.baseOption
			);
			param._log.go_count_time = new Date() - param._log.go_count_time;
			total = (data && data['total']) || 0;
			param._log.go_count = total;
			log(20, '结束查询Go接口,获取记录总数:%d, 消耗时间:%d', total, param._log.go_count_time);
		}
		else {
			param._log.mongo_count_time = new Date();
			// 统计符合要求的业务数据数量
			log(20, '开始查询mongo ,获取记录总数');
			total = yield mongoQuery.count();
			// 如果需要查询默认记录0,total总数增加1
			if (options.zero)
			{
				total++;
			}

			param._log.mongo_count_time = new Date() - param._log.mongo_count_time;
			param._log.mongo_count = total;
			log(20, '结束查询mongo ,获取记录总数:%d, 消耗时间:%d', total, param._log.mongo_count_time);
		}


		// 正常有数据, 计算分页, 查询 Go 数据
		if(total > 0) {
			if(offset >= total) {
				// 分页超界外
				param.page = Math.ceil(total/param.size);
				offset = (param.page -1) * param.size;
			}
			// 分页大小
			goOptions['limit'] = param.size;
			// 记录偏移计数
			goOptions['offset'] = offset;

			// 有指标排序并且使用的是维度过滤条件, 提供所有记录id, 方便关联查询
			if(goOptions['must_keys'] && dimWhere){
				if (!mongoIds){
					param._log.bygo_getId_time = new Date();
					mongoIds = yield mongoQuery.getResult(param.modelKey);
					param._log.bygo_getId_time = new Date() - param._log.bygo_getId_time;
				}

				goOptions['out_set_key_values'] = mongoIds;
				goCond[param.metricKey] = {'$in': mongoIds};
			}

			// 分页查询统计数据
			param._log.go_page_time = new Date();
			log(20, '开始查询分页go接口获取统计数据');
			data = yield self.queryGo(goOptions, param.baseOption);
			param._log.go_page_time = new Date() - param._log.go_page_time;
			log(20, '结束查询分页go接口获取统计数据, 消耗时间: %d', param._log.go_page_time);
		} else {
			param.page = 1;
			data = false;
		}
	}
	else {
		// 无分页查询所有数据
		log(20, '开始查询无分页go接口获取统计数据');
		data = yield self.queryGo(goOptions, param.baseOption);
		total = (data && data['item_count']) || 0;
		log(20, '结束查询无分页go接口获取统计数据 总数为：' + total);
	}

	if (data) {
		// 返回结果数据
		goItems = data['items'];
		getAmount = total > 0;

		goIds = [];
		goItems.forEach(function(item) {
			if (item.hasOwnProperty(param.metricKey)){
				goIds.push(item[param.metricKey]);
			}
		});

		if (goIds.length) {
			// 查询mongo业务数据
			mongoIds = {};
			mongoIds[param.modelKey] = {'$in': util.unique(goIds)};
			param._log.mongo_goId_time = new Date();
			log(20, '开始根据Go的_id查询本地的Mongo数据');
			mongoItems = keyIndexArray(
				param.modelKey,
				(yield mongoQuery.where(mongoIds).getResult())
			);
			param._log.mongo_goId_time = new Date() - param._log.mongo_goId_time;
			log(20, '结束根据Go的_id查询本地的Mongo数据, 消耗时间:%d', param._log.mongo_goId_time);
		}else {
			mongoItems = new Map();
		}
		// 组合资料, 以 Go 数据为准
		while (item = goItems.shift()){
			let id = item[param.metricKey];
			item[param.metricKey] = undefined;
			id = Int64.isInt64(id) ? id.toString() : id;
			if (mongoItems.has(id)){
				item = extend(item, mongoItems.get(id));
				//mongoItems.delete(id);
			}else {
				item[param.modelKey] = id;
			}
			items.push(item);
		}
	}
	else if(total > 0) {
		// 查询失败则生成空数据记录
		// 查询 Mongo业务 数据
		let where = param.where;
		param._log.mongo_time = new Date();
		if(hasPage) {
			where.size = param.size;
			where.offset = (param.page - 1) * param.size;
			mongoItems = yield mongoQuery.getResult(null, where);
		}else {
			mongoItems = yield mongoQuery.getResult();
		}
		param._log.mongo_time = new Date() - param._log.mongo_time;
		// 组合指标
		if (mongoItems && mongoItems.length){
			// 生成 0 指标记录
			goIds = {};
			for (var key in param.metricFields){
				goIds[param.metricFields[key]] = 0;
			}
			while (key = mongoItems.shift()){
				items.push(extend(key, goIds));
			}
		}
	}

	goItems = goIds = mongoItems = mongoIds = null;

	// id为0的默认记录，填充默认名称
	if (options && options.zero && options.zero_name_field && items && items.length)
	{
		for (let item of items)
		{
			if(item._id == 0)
			{
				item[options.zero_name_field] = options.zero;
				break;
			}
		}
	}

	return {
		getAmount: getAmount
		,items: items
		,total: total
		,goCond: goCond
	}
}

function *_query_by_mongo(options, param, mongoQuery) {
	var self = this;
	var goCond, goItems, goIds, mongoItems, mongoIds, offset;
	// 指标维度过滤条件, 代替mongo id范围过滤
	var dimWhere = options['dim_where'];
	// 指标附加过滤条件
	var addOnWhere = options['addon_where'];

	var hasPage = param.hasPage;
	var where = param.where;
	var goOptions;
	var items = [], getAmount, total;

	// 统计符合要求的业务数据数量
	param._log.mongo_count_time = new Date();
	log(20, '开始查询本地符合条件的mongo记录总数');
	total = yield mongoQuery.count();
	// 如果需要查询默认记录0,total总数增加1
	if (options.zero)
	{
		total++;
	}
	param._log.mongo_count_time = new Date() - param._log.mongo_count_time;
	param._log.mongo_count = total;
	log(20, '结束查询本地符合条件的mongo记录总数:%d, 消耗时间:%d', total, param._log.mongo_count_time);
	// 有维度过滤条件, 直接使用维度过滤条件
	if(total !== 0) {
		goCond = extend(dimWhere, addOnWhere);
		// mongo分页查询,
		if(hasPage) {
			offset = (param.page - 1) * param.size;
			if(offset >= total) {
				// 分页超界外
				param.page = Math.ceil(total/param.size);
				offset = (param.page - 1) * param.size;
			}

			// 设置分页过滤
			where.size = param.size;
			where.offset = offset;
		}
		// 获取对应的 Mongo 记录数据
		param._log.mongo_local_time = new Date();
		log(20, '开始查询本地符合条件的mongo记录');
		mongoItems = yield mongoQuery.getResult(null, hasPage ? where : null);
		param._log.mongo_local_time = new Date() - param._log.mongo_local_time;
		log(20, '结束查询本地符合条件的mongo记录, 消耗时间:%d', param._log.mongo_local_time);

		// 默认0记录
		if (options.zero)
		{
			mongoItems.unshift({
				'_id': 0,
				[options.zero_name_field]: options.zero
			});
		}

		//有指标字段, 调用 Go 查询接口查询数据
		if (param.metricFields.length) {
			// 如果分页或者没有维度查询条件, 限制业务记录范围
			if (hasPage || !dimWhere){
				mongoIds = [];
				mongoItems.forEach(function(item){
					if (item[param.modelKey]){
						mongoIds.push(item[param.modelKey]);
					}
				});
				goCond[param.metricKey] = {'$in': mongoIds};
			}

			// 查询统计数据
			goOptions = {
				'metrics': param.metricFields,
				'conds': goCond,
				'must_keys': 0,
				'in_keys_order': 0,
				'limit': CONST.MAX_QUERY_LIMIT,
				'use_item': 1
			};

			param._log.go_items_time = new Date();
			log(20, '开始查询统计数据');
			var data = yield self.queryGo(goOptions, param.baseOption);
			param._log.go_items_time = new Date() - param._log.go_items_time;
			log(20, '结束查询统计数据, 消耗时间:%d', param._log.go_items_time);

			if (data) {
				// 请求查询服务器成功, 转换项目为id下标数组
				goItems = keyIndexArray(param.metricKey, data['items']);
				// 如果是分页查询, 设置不分页重新查询条件给总计查询使用
				getAmount = param.hasAmount;
				if (hasPage && param.hasAmount) {
					where.size = 0;
					where.offset = 0;
					param._log.mongo_getId_time = new Date();
					goCond[param.metricKey] = {'$in': (yield mongoQuery.getResult(param.modelKey, where))};
					param._log.mongo_getId_time = new Date() - param._log.mongo_getId_time;
				}
			} else {
				goItems = new Map();
			}

			// 合并数组, 以mongo数据为基础
			goIds = {};
			param.metricFields.forEach(function(name) {
				goIds[name] = 0;
			});

			var id, item, metrics;
			while (item = mongoItems.shift()){
				id = item[param.modelKey];
				id = Int64.isInt64(id) ? id.toString() : id;
				if (goItems.has(id)) {
					metrics = goItems.get(id);
					goItems.delete(id);
				} else {
					metrics = goIds;
				}

				items.push(extend(item, metrics));
			}
		} else {
			// 没有指标查询, 直接返回mongo数据
			items = mongoItems;
		}
	}
	else {
		param.page = 1;
	}

	goItems = goIds = mongoItems = mongoIds = null;

	return {
		getAmount: getAmount
		,items: items
		,total: total
		,goCond: goCond
	}
}

DataCenterGo.prototype.queryWithException = generator(function*(options) {
	var self = this;
	self.debug('query_param', options);
	var type = options && options['type'];
	var query = getConfig('querys.' + type);
	if(!query) {
		setError(self, 10000001);
	}

	// 重置错误状态
	setError(self, 0);

	var modelKey = query['model_key']
		,metricKey = query['id']
		,metricKeys = query['keys']
		,metricDims = query['dims'] || []
		,fullRecord = options['full_record'];

	var where = {
		'query': options['where'] || {}
	};

	var baseOption = extend(options['base'], {
		'keys': options.keys || metricKeys,
		'dims': metricDims,
		'query_type': options['ctype'] || 'default'
	});

	if(query.timeout)
	{
		baseOption.timeout = query.timeout;
	}

	// 指标过滤条件
	var optWhere = options['metric_where'];
	var hasMetricWhere = false;
	var metricWhere = {};
	if (optWhere) {
		for (let key in optWhere){
			if(optWhere.hasOwnProperty(key)) {
				switch (key){
					case 'begintime':
					case 'endtime':
						baseOption[key.slice(0,-4)+'_'+key.slice(-4)] = optWhere[key];
						break;
					default:
						metricWhere[key] = optWhere[key];
						hasMetricWhere = true;
						break;
				}
			}
		}
	}


	// 转换组合要查询的指标
	var fields = yield processFields(query, options);

	// 过滤查询字段
	where['fields'] = fields['mongo'];

	// 将自定义指标转换为维度加入keys
/*	if(fields['metric'].length){
		fields['metric'].forEach(f => {
			if(f.match('metric_')){
				baseOption.keys.push(f.replace(/metric/,'dim'));
			}
		});
	}*/

	// 排序字段参数处理
	var sort = processOrder(options);
	if(sort['mongo']) {
		where['sort'] = sort['mongo'];
	}

	var param = {
		metricWhere: metricWhere
		,hasMetricWhere: hasMetricWhere
		,baseOption: baseOption
		,metricFields: fields.metric
		,page: options.page
		,size: options.size
		,hasPage: (options.page > 0 && options.size >0)
		,where: where
		,modelKey: modelKey
		,metricKey: metricKey
		,metricKeys: metricKeys
		,metricDims: metricDims
		,fullRecord: fullRecord
		,sort: sort
	};

	// 分页参数处理
	param.hasAmount = defaultValue(options, 'query_amount', true);
	param.hasItems = defaultValue(options, 'query_items', true);

	param._log = {
		collection: query.model
		,count_time: 0
		,go_items_time: 0 // time
		,go_amount_time: 0 //time
		,go_page_time: 0
		,mongo_page_time: 0
		,metrics: fields.metric
		,mongo_count_time: 0
	};
	var data, mongoQuery;

	if(param.hasItems) {
		try {
			// 业务数据分页查询, 查出需要展现的分页id记录数组
			switch (query.query_type){
				case 'aggregate':
					mongoQuery = new DB.mongoAggregate(query, where, options.aggregate);
					break;
				case 'dynamic':
					break;
				default:
					mongoQuery = new DB.mongoFind(query, where);
					break;
			}


			// 有mongo记录, 判断查询条件决定使用何种查询方式
			var byGo = (hasMetricWhere || sort.metric || (metricKeys && metricKeys.length >= 2)) && param.metricFields.length;
			if(options.hasOwnProperty('byGo'))
			{
				byGo = options.byGo;
			}

			param._log.query_by_go = byGo;
			if(byGo)
			{
				data = yield _query_by_go.call(self, options, param, mongoQuery);
			}
			else
			{
				data = yield _query_by_mongo.call(self, options, param, mongoQuery);
			}

		}
		catch(e) {
			//释放mongo 实例
			mongoQuery && mongoQuery.free && mongoQuery.free();
			mongoQuery = null;
			throw e;
		}
	}
	//释放mongo 实例
	mongoQuery && mongoQuery.free && mongoQuery.free();
	mongoQuery = null;

	var amount;
	var goCond = data.goCond;
	var total = data.total;
	var getAmount = data.getAmount;
	var items = data.items;
	data = null;

	// 是否需要查询汇总数据, 有指标项才查询
	if(param.hasAmount && param.metricFields.length) {
		if(getAmount) {
			// 需要查询远程服务器
			var goOptions = {
				'metrics': param.metricFields,
				'conds': goCond,
				'use_amount': 1
			};

			param._log.go_amount_time = new Date();
			data = yield self.queryGo(goOptions, baseOption);

			param._log.go_amount_time = new Date() - param._log.go_amount_time;
			if(data) {
				amount = data['amount'];
			}
		}
		if(!amount) {
			amount = {};
			param.metricFields.forEach(function(name) {
				amount[name] = 0;
			});
		}
	}

	// 返回数据
	var result = {'total': total};

	if(param.hasPage) {
		result['page'] = param.page;
		result['size'] = param.size;
	}
	if(param.hasItems) {
		result['items'] = items;
	}
	if(param.hasAmount) {
		result['amount'] = amount || {};
	}

	if(self.$debug) {
		result['debug'] = self.debug();
	}

	_set_log_time_(param._log);
	go_log.unshift(param._log);
	go_log.splice(100, 1);
	util.log(101, `DataCenterGo Query Log: ${JSON.stringify(param._log)}`);
	return result;
});

function _set_context(sign, field, param) {
	let set = false;
	if(sign) {
		set = radon.getContextData(sign);
	}
	else {
		set = true;
	}

	if(set) {
		let data = radon.getContextData(field) || [];
		data.push(param);
		radon.setContextData(field, data);
	}
}

let __go_param_debug__ = _set_context.bind(this, '__debug__', 'go_debug_request');
let _set_log_time_ = _set_context.bind(this, '', 'dataCenter_go_log_time');


/**
 * 查询Go数据服务器
 * @param  Array  $options 查询参数数组对象
 * @param  Array  $base    <可选> 其他基础参数对象
 * @return Array           返回查询调用结果对象, 失败时返回false
 */
DataCenterGo.prototype.queryGo = function(opts, base) {
	var self = this;
	return util.promise(function(ok, fail) {
		// 根据查询类型参数隐藏溢价相关指标
		// 合并其他基础参数
		var options = extend(base, opts);
		if(!options.limit) {
			options.limit = 1000000;
		}

		options.debug = 1;

		var remote_config = self.$remote_config;
		self.debug('go_param', options);

		var body = radon_json.stringify(options);
		// 转换指标名称
		var maps = getConfig('maps');

		var revertMap = new Map();
		if (maps.size > 0) {
			var regx = '';
			for (var key of maps.keys()){
				regx += key + '|';
			}
			regx = new RegExp('"(' + regx.slice(0,-1) + ')"', 'g');
			body = body.replace(regx, function(match, key){
				var mapKey = maps.get(key);
				revertMap.set(key, mapKey);
				return '"' + mapKey + '"';
			});
		}

		// 记录发送的请求数据
		self.debug('query_json', body);
		__go_param_debug__(body);
		var hasTimeout = false;
		var req = http.request({
			hostname: remote_config.hostname,
			port: remote_config.port,
			path: remote_config.path,
			method: 'POST',
			headers: {
				'Content-Type': 'application/json',
				'Content-Length': Buffer.byteLength(body)
			}
		}, function(res)
		{
			var chunks = [];
			res.on('data', function(chunk) {
				chunks.push(chunk);
			});
			res.on('end', function() {
				try {
					if (hasTimeout) {
						return;
					}
					var str = Buffer.concat(chunks).toString();

					// 读取服务器数据, 解析JSON数据
					var data = radon_json.parse(str);

					// 保存调试信息
					self.debug('go_result', data);
				}
				catch(e) {
					self.debug('query_response', data);

					util.log(104, `查询服务器返回数据无效`);
					util.log(104, JSON.stringify({query: body, json: ret}));

					return fail(setError(self, 10000004, true));
				}

				if (data && data['suc']) {
					// 转换映射数据和记录数据结构
					var fields = new Map();
					var result_fields = data.item_fields || [];
					var keys;

					// 转换字段名称
					if (revertMap.size > 0) {
						// 转换items字段列表
						keys = util.unique([].concat(
							options.keys || [],
							options.dims || [],
							options.metrics || []
						)).sort();

						for (var key in keys){
							if(keys.hasOwnProperty(key)) {
								key = keys[key];
								fields.set(key, result_fields.indexOf(revertMap.get(key) || key));
							}
						}

						// 转换汇总数据
						if (options.use_amount){
							var amount = {};
							options.metrics.forEach(function(key) {
								var field = revertMap.get(key) || key;
								amount[key] = data.amount[field] || 0;
							});

							data.amount = amount;
						}
					}
					util.promise((resolve, reject) => {
						if (data.item_count > 0) {
							var items = [];
							var customDim = [];
							var item, i, key, value;
							if (options.no_combine){
								if (keys){
									while (item = data.items.shift()){
										value = {};
										for (i=keys.length; i --> 0;){
											key = keys[i];
											value[key] = item[fields.get(key)] || 0;
										}
										items.push(value);
									}
								}else {
									items = data.items;
								}
							}
							else {
								// 转换指标格式
								if (!keys){
									// 普通查询转换表
									for (var key in result_fields){
										if(result_fields.hasOwnProperty(key)) {
											fields.set(result_fields[key], key);
										}
									}
									keys = result_fields.sort();
								}
								while (item = data.items.shift()){
									value = {};
									for (i=keys.length; i --> 0;){
										key = keys[i];
										value[key] = item[fields.get(key)] || 0;
										if(key.match('dim_') && value[key] != 0){
											customDim.push(value[key]);
										}
									}
									items.push(value);
								}
							}

							if (keys){
								data.item_fields = keys;
							}

							// 转换自定义维度数据
							if(customDim.length){
								util.log(101, `自定义维度映射查询开始`);

								var m = url.parse(getConfig('mappingUrl'));

								var mapReq = http.request({
									host: m.hostname,
									port: m.port,
									path: m.path,
									method: 'POST',
									headers: {
										'Content-Type': 'application/json',
									},

								}, (res) =>{
									var mapData = '';

									res.on('data', (chunk) => {
										mapData += chunk;
									});

									res.on('end', () => {
										mapData = JSON.parse(mapData);
										if(mapData && mapData.is_suc){
											util.log(101, `自定义维度映射查询完成`);
											var dimMap = mapData.data;
											for (var item of items){
												var fields = Object.keys(item);
												for(var field of fields){
													if(field.match('dim_')){
														item[field] = dimMap[item[field].toString()];
													}
												}
											}
										}
										util.log(101, `自定义维度映射处理完成`);
										data.items = items;
										resolve(data);
									});
								}).on('error',function(e){
									util.log(104, `自定义维度映射查询失败`);
									util.log(104, JSON.stringify({query: customDim, data: e}));
								});

								mapReq.write(JSON.stringify({'ids': customDim}));
								mapReq.end();
							}
							else{
								data.items = items;
								resolve(data);
							}
						}
						else {
							resolve(data);
						}
						}
					).then(function(data){
						ok(data);
					});
				}

				else {
					// 查询服务器处理失败
					util.log(104, `查询服务器处理失败`);
					util.log(104, JSON.stringify({query: body, data: data}));

					fail(setError(self, 10000005, true));
				}
			})
		});

		req.on('error', function(e) {
			util.log(104, `查询服务器请求失败`);
			util.log(104, JSON.stringify(e));

			fail(setError(self, 10000003, true));
		});

		var timeout = options.timeout;
		if (timeout) {
			req.setTimeout(timeout, function() {
				hasTimeout = true;
				req.abort();
				util.log(104, JSON.stringify({reason: "请求超时，主动断掉", query: body}));
				fail(setError(self, 10000007, true));
			});
		}

		req.write(body);
		req.end();
	});
};


/**
 * 查询返回所有符合条件的Mongo记录
 * @param  String $collection 要查询的集合名称
 * @param  Object $param      查询条件对象
 * @return Array              返回符合条件的查询记录, 没有记录时返回空数组
 */
DataCenterGo.prototype.findAll = function(collection, param, field){
	return util.promise(function(ok, fail) {
		var find = new DB.mongoFind({'model': collection}, param);
		find.getResult(field, param).then(
			function(data) { ok(data); find.free();}
			,function(err) { fail(err); find.free();}
		);
	});
};

/**
 * 私有功能函数
 */

// 配置文件读取函数
var config_file = radon.config('app.datacenter_config');
function getConfig(){
	var args = util.toArray(arguments);
	args.unshift(config_file);
	return util.config.apply(util, args);
}
exports.config = getConfig;

// 合并扩镇一个新对象
function extend(){
	var args = arguments;
	var param = [{}];

	for (var i=0; i<args.length; i++){
		if (args[i] instanceof Object){
			param.push(args[i]);
		}
	}

	return util.extend.apply(util, param);
}

// 获取参数默认值
function defaultValue(data, property, def){
	if (data.hasOwnProperty(property)){
		return data[property];
	}else {
		return def;
	}
}

// 设置错误代码
function setError(self, code, slience){
	self.$error_code = code;
	if (code && !slience){
		radon.throw(code);
	}

	return radon.getError(code);
}

/**
 * 处理查询选项中的order排序信息, 返回对应mongo和metric查询的排序参数
 * @param  Array $options 查询参数对象
 * @return Array          返回mongo和metric对应的查询排序参数
 */
function processOrder(options) {
	var param = options.order;
	var mongoOrder;
	var metricOrder;
	var metric_field = hawkConfig.metric_field || [];
	if(param) {
		param = param.split(',');
		param.forEach(function(item) {
			item = item.split('|');
			if(item.length === 2) {
				if(isMetric(item[0]) || ~metric_field.indexOf(item[0])) {
					if(!metricOrder) {
						metricOrder = [];
					}

					metricOrder.push({
						name: item[0]
						,asc: +item[1] == 1
					});
				}
				else {
					if(!mongoOrder) {
						mongoOrder = {};
					}
					mongoOrder[item[0]] = (+item[1] == 1 ? 1: -1);
				}
			}
		});
	}

	return {
		'mongo': mongoOrder,
		'metric': metricOrder
	};
}

DataCenterGo.prototype.processOrder = processOrder;

/**
 * 处理查询选项中的fields字段信息, 返回对应mongo和metric查询的字段
 * @param  Array $query   查询维度配置对象
 * @param  Array $options 查询参数对象
 * @return Array          返回mongo和metric对应的查询字段列表
 */
var processFields = generator(function*(query, options) {
	var excludes = [];
	var param = options.fields;
	if(!param || !Array.isArray(param)) {
		// 没有配置, 使用默认配置
		param = query.fields.concat(query.metrics)
	}
	else {
		var mode = +param[0];
		if(!isNaN(mode)) {
			// 指定了补全模式, 根据模式值处理合并
			param.shift();
		}
		else {
			// 普通字段, 默认值为合并所有
			mode = -1;
		}

		// 0 - 不合并, &1 - 合并mongo, &2 - 合并指标
		if(mode & CONST.INCLUDE_MONGO_FIELDS) {
			var configFields = query.fields;
			if((!configFields || !configFields.length) && query.model) {
				try {
					var model = ModelDB.model(query.model);
					if(model) {
						configFields = Object.keys(model.$schema.$fields);
					}
				}
				catch(e) {
					configFields = null;
				}
				if(query.exclude_fields && query.exclude_fields.length) {
					excludes = excludes.concat(query.exclude_fields);
				}
			}

			if(configFields && configFields.length) {
				param = param.concat(configFields);
			}
		}
		if(mode & CONST.INCLUDE_METRIC_FIELDS) {
			param = param.concat(query.metrics);
		}

		param = util.unique(param);
	}

	// 转换排查字段参数
	if (Array.isArray(options.exclude_fields)) {
		options.exclude_fields.forEach(function(name) {
			if(name[0] == '{') {
				var group = getConfig('metrics.'+ name.slice(1, -1));
				if(Array.isArray(group)) {
					excludes = excludes.concat(group);
				}
			}
			else {
				excludes.push(name);
			}
		});
	}

	// 转换指标宏, 拆分mongo字段
	var mongoFields = ['_id'];
	var metricsFields = [];
	var right_map = radon.use('Metrics_Map');
	var user = yield include.getUser();
	var right = user.getRights();
	right = right.join('|') + '|';
	param.forEach(function(name) {
		if(name[0] == '{')
		{
			var group = getConfig('metrics.'+ name.slice(1, -1));
			if(sysUtil.isArray(group))
			{
				for(let metric of group)
				{
					if((user.isMaster() || checkRight(right, right_map, metric, options.skipMetricCheck)))
					{
						metricsFields.push(metric);
					}
				}
			}
		}
		else if(
			isMetric(name) &&
			(user.isMaster() || checkRight(right, right_map, name, options.skipMetricCheck)))
		{
			metricsFields.push(name);
		}
		else if(!isMetric(name))
		{
			mongoFields.push(name);
		}
	});

	// 排除指定的字段
	if(excludes && excludes.length) {
		mongoFields = util.difference(mongoFields, excludes);
		metricsFields = util.difference(metricsFields, excludes);
	}

	return {
		'mongo': util.unique(mongoFields),
		'metric': util.unique(metricsFields)
	};
});

function checkRight(right, right_map, name, skip)
{
	if(skip)
	{
		return true;
	}
	else
	{
		let r;
		if(right_map.hasOwnProperty(name))
		{
			r = right_map[name];
			r = r.indexOf('.') > -1 ? `${r}|` : `.${r}|`;
		}
		else
		{
			r = `.${name}|`;
		}

		return ~right.indexOf(r);
	}
}

DataCenterGo.prototype.processFields = processFields;

/**
 * 判断是否为指标字段
 * @param  String  $name 字段名称
 * @return boolean       是否为指标字段
 */
var metricCache;
function isMetric(name) {
	if(!metricCache) {
		metricCache = getConfig('captions') || {};
	}

	return !!metricCache[name];
}

exports.isMetric = isMetric;

DataCenterGo.isMetric = isMetric;

/**
 * 以数组中的某个属性值作为下标转换数组
 * @param  String $key  下标属性名称
 * @param  Array  $list 待转换的数组对象
 * @return Array        返回转换后的数组对象
 */
function keyIndexArray(key, list) {
	var item, result = new Map();
	var id;
	while (item = list.pop()){
		id = item[key];
		if (Int64.isInt64(id)){
			id = id.toString();
		}

		result.set(id, item);
	}
	return result;
}
